package de.tuhh.simsearch.model.persistence.usermgmt.entities;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;

/**
 * Entity implementation class for the user entity. The user entity represents the user table in the database. The name
 * of the user is the primary key to the database. The user has a password, which is stored as a hash in the database.
 * Also, the user can have a set of roles, a set of created groups, joined groups, and requested groups and a set of
 * preferences.
 * 
 * @author SimSearch 1
 * 
 * @see RoleEntity
 * @see GroupEntity
 * @see PreferenceEntity
 */
@Entity
public class UserEntity implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id private String name;

  private String passwd;

  @ManyToMany private Set<RoleEntity> roles = new HashSet<RoleEntity>();

  @OneToMany(mappedBy = "administrator", cascade = CascadeType.PERSIST) private Set<GroupEntity> createdGroups = new HashSet<GroupEntity>();

  @ManyToMany @JoinTable(name = "USERENTITY_JOINEDGROUP") private Set<GroupEntity> joinedGroups = new HashSet<GroupEntity>();

  @ManyToMany @JoinTable(name = "USERENTITY_REQUESTEDGROUP") private Set<GroupEntity> requestedGroups = new HashSet<GroupEntity>();

  @ManyToMany private Set<PreferenceEntity> preferences = new HashSet<PreferenceEntity>();

  protected UserEntity() {

  }

  /**
   * The class constructor, sets the user name of the user entity
   * 
   * @param username
   *          the user name for this user
   */
  public UserEntity(String username) {

    setName(username);
  }

  /**
   * Gets the name of the user who is represented by this enitity
   * 
   * @return the name of the user
   */
  public String getName() {

    return name;
  }

  /**
   * Sets the name of the user
   * 
   * @param name
   *          the name of the user
   */
  public void setName(String name) {

    this.name = name;
  }

  /**
   * Gets the set of the groups which were created by this user. The set is unmodifiable.
   * 
   * @return the set of created groups
   */
  public Set<GroupEntity> getCreatedGroups() {

    return Collections.unmodifiableSet(createdGroups);
  }

  /**
   * Adds a new group to the set of created groups of this user
   * 
   * @param g
   *          the group entity to be added
   */
  public void addCreatedGroup(GroupEntity g) {

    this.createdGroups.add(g);
    if (g.getAdministrator() != this) {

      g.setAdministrator(this);
    }
  }

  /**
   * Removes a group from the set of created groups.
   * 
   * @param g
   *          the group entity to remove
   */
  public void removeCreatedGroup(GroupEntity g) {

    this.createdGroups.remove(g);
    if (g.getAdministrator() == this) {

      g.removeAdministrator();
    }
  }

  protected void setCreatedGroups(Set<GroupEntity> createdGroups) {

    this.createdGroups = createdGroups;
  }

  /**
   * Gets the unmodifiable set of preference of this user
   * 
   * @return the set of preferences
   */
  public Set<PreferenceEntity> getPreferences() {

    return Collections.unmodifiableSet(preferences);
  }

  /**
   * Adds a preference to the set of preferences of this user
   * 
   * @param p
   *          the preference entity to add
   */
  public void addPreference(PreferenceEntity p) {

    this.preferences.add(p);
    if (!(p.getPreferenceSubscriber().contains(this))) {

      p.addPreferenceSubscriber(this);
    }

  }

  /**
   * Removes a preference entity from the set of preferences of this user
   * 
   * @param p
   *          the preference entity to remove
   */
  public void removePreference(PreferenceEntity p) {

    this.preferences.remove(p);
    if (p.getPreferenceSubscriber().contains(this)) {

      p.removePreferenceSubscriber(this);
    }
  }

  protected void setPreferences(Set<PreferenceEntity> preferences) {

    this.preferences = preferences;

  }

  /**
   * Gets the set of groups joined by this user
   * 
   * @return the unmodifiable set of joined groups
   */
  public Set<GroupEntity> getJoinedGroups() {

    return Collections.unmodifiableSet(joinedGroups);
  }

  /**
   * Adds a group to the set of joined groups
   * 
   * @param g
   *          the group entity to add
   */
  public void addJoinedGroup(GroupEntity g) {

    this.joinedGroups.add(g);
    if (!(g.getMembers().contains(this))) {

      g.addMember(this);
    }
  }

  /**
   * Removes a group from the set of joined groups
   * 
   * @param g
   *          the group entity to remove
   */
  public void removeJoinedGroup(GroupEntity g) {

    this.joinedGroups.remove(g);
    if (g.getMembers().contains(this)) {

      g.removeMember(this);
    }
  }

  protected void setJoindedGroups(Set<GroupEntity> joinedGroups) {

    this.joinedGroups = joinedGroups;
  }

  /**
   * Gets the set of roles of this user
   * 
   * @return the unmodifiable set of roles
   */
  public Set<RoleEntity> getRoles() {

    return Collections.unmodifiableSet(roles);
  }

  /**
   * Adds a role to the set of roles of this user
   * 
   * @param r
   *          the role entity to add
   */
  public void addRole(RoleEntity r) {

    this.roles.add(r);
    if (!(r.getUsers().contains(this))) {

      r.addUser(this);
    }
  }

  /**
   * Removes a role from the set of roles of this user
   * 
   * @param r
   *          the role entity to remove
   */
  public void removeRole(RoleEntity r) {

    this.roles.remove(r);
    if (r.getUsers().contains(this)) {

      r.removeUser(this);
    }
  }

  protected void setRoles(Set<RoleEntity> roles) {

    this.roles = roles;
  }

  /**
   * Gets the set of requested groups by this user
   * 
   * @return the unmodifiable set of requested groups
   */
  public Set<GroupEntity> getRequestedGroups() {

    return Collections.unmodifiableSet(requestedGroups);
  }

  protected void setRequestedGroups(Set<GroupEntity> requestedGroups) {

    this.requestedGroups = requestedGroups;
  }

  /**
   * Adds a group entity to the set of groups requested by this user
   * 
   * @param g
   *          the group entity to add
   */
  public void addRequestedGroup(GroupEntity g) {

    this.requestedGroups.add(g);
    if (!(g.getPendingUsers().contains(this))) {

      g.addPendingUser(this);
    }
  }

  /**
   * Removes a group from the list of groups requested by this user
   * 
   * @param g
   *          the group entity to remove
   */
  public void removeRequestedGroup(GroupEntity g) {

    this.requestedGroups.remove(g);
    if (g.getPendingUsers().contains(this)) {

      g.removePendingUser(this);
    }
  }

  protected void setJoinedGroups(Set<GroupEntity> joinedGroups) {

    this.joinedGroups = joinedGroups;
  }

  /**
   * Gets the password of this user
   * 
   * @return the hashed password
   */
  public String getPassword() {

    return passwd;
  }

  /**
   * Sets the password
   * 
   * @param password
   *          the password to set
   */
  public void setPassword(String password) {

    this.passwd = password;
  }

}
